#!/usr/bin/env python3

import os
import sys

def escapeHTML(text):
	tmp = text.replace("<", "&lt;")
	tmp = tmp.replace(">", "&gt;")
	return tmp

class DocValue(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d

	def genHTML(self):
		return "<b style='padding-left: 40px'>"+escapeHTML(self.name)+"</b> - "+escapeHTML(self.description)+"<br>"

class DocType(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d
		self.values = []

	def genHTML(self):
		s = "<p><b>type - "+escapeHTML(self.name)+"</b> - "+escapeHTML(self.description)+"<br>"
		if len(self.values) > 0:
			s += "<b style='padding-left: 20px'>values:</b><br>"
			for i in self.values:
				s += i.genHTML()
		return s
	
class DocMember(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d

	def genHTML(self):
		sp = self.name.split("=")[0].strip(" ").split(" ")
		s = ""
		for i in sp[0:len(sp)-1]:
			s += i+" "
		return "<b style='padding-left: 40px'></b>"+escapeHTML(s)+"<b>"+escapeHTML(sp[len(sp)-1])+"</b> - "+escapeHTML(self.description)+"<br>"

class DocReturn(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d

	def genHTML(self):
		return "<b style='padding-left: 60px'></b>returns "+escapeHTML(self.name)+" - "+escapeHTML(self.description)+"<br>"

class DocArgument(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d

	def genHTML(self):
		return "<b style='padding-left: 60px'></b>takes argument "+escapeHTML(self.name)+" - "+escapeHTML(self.description)+"<br>"

class DocMethod(object):
	def __init__(self, n, p, d):
		self.name = n
		self.prototype = p
		self.description = d
		self.returns = None
		self.arguments = []

	def genHTML(self):
		s = ""
		if not " " in self.prototype.split("(")[0]:
			s = "<b style='padding-left: 40px'>"+escapeHTML(self.prototype.split("(")[0])+"</b>("+escapeHTML(self.prototype.split("(")[1])+" - "+escapeHTML(self.description)
		else:
			s = "<b style='padding-left: 40px'></b>"+escapeHTML(self.prototype.split(" ")[0])+" <b>"+escapeHTML(self.name)+"</b>("+escapeHTML(self.prototype.split("(")[1])+" - "+escapeHTML(self.description)
		s += "<br>"
		if self.returns != None:
			s += self.returns.genHTML()
		for i in self.arguments:
			s += i.genHTML()
		return s

class DocStruct(object):
	def __init__(self, n, d):
		self.name = n
		self.description = d

		self.members = []
		self.methods = {}
		self.methorder = []

	def genHTML(self):
		s = "<p><b>struct - "+escapeHTML(self.name)+"</b> - "+escapeHTML(self.description)+"<br>"
		if len(self.members) > 0:
			s += "<b style='padding-left: 20px'>members:</b><br>"
			for i in self.members:
				s += i.genHTML()
		if len(self.methorder) > 0:
			s += "<b style='padding-left: 20px'>methods:</b><br>"
			for i in self.methorder:
				s += self.methods[i].genHTML()
		return s

class DocClass(DocStruct):
	def genHTML(self):
		s = "<p><b>class - "+escapeHTML(self.name)+"</b> - "+escapeHTML(self.description)+"<br>"
		if len(self.members) > 0:
			s += "<b style='padding-left: 20px'>members:</b><br>"
			for i in self.members:
				s += i.genHTML()
		if len(self.methorder) > 0:
			s += "<b style='padding-left: 20px'>methods:</b><br>"
			for i in self.methorder:
				s += self.methods[i].genHTML()
		return s
	
def extractDocumentationFrom(path):
	f = open(path, "r")
	page = f.read()
	f.close()

	namespace = ""
	additions = []
	types = {}
	structs = {}
	methods = {}
	globalMethods = []
	order = []

	def extractFromLine(line, p, f):
		ns = ""
		if p == 0 and line[0:3] == "///":
			l = escapeHTML(line[3:])
			l = l.replace("{{", "<div id='code'>")
			l = l.replace("}}", "</div>")
			additions.append(l)

		cmd = line[line.find("//")+2:]
		cmd = [i.strip(" \t") for i in cmd.split(":")]
		cmd[0] = cmd[0].split(" ")

		try:
			if cmd[0][0] == "valueof" and p == 1:
				if line.split(" ")[0] == "#define":
					types[cmd[0][1]].values.append(DocValue(line.split(" ")[1], cmd[1]))
			elif cmd[0][0] == "type" and p == 0:
				types[cmd[0][1]] = DocType(cmd[0][1], cmd[1])
				order.append(cmd[0][1])
			elif cmd[0][0] == "struct" and p == 0:
				structs[cmd[0][1]] = DocStruct(cmd[0][1], cmd[1])
				order.append(cmd[0][1])
			elif cmd[0][0] == "class" and p == 0:
				structs[cmd[0][1]] = DocClass(cmd[0][1], cmd[1])
				order.append(cmd[0][1])
			elif cmd[0][0] == "memberof" and p == 1:
				structs[cmd[0][1]].members.append(DocMember(line.split(";")[0].strip(" \t"), cmd[1]))
			elif cmd[0][0] == "returnof" and p == 2:
				try:
					structs[methods[cmd[0][1]]].methods[cmd[0][1]].returns = DocReturn(cmd[1], cmd[2])
				except KeyError:
					for i in range(0, len(globalMethods)):
						if globalMethods[i].name == cmd[0][1]:
							globalMethods[i].returns = DocReturn(cmd[1], cmd[2])
							break
			elif cmd[0][0] == "argof" and p == 2:
				try:
					structs[methods[cmd[0][1]]].methods[cmd[0][1]].arguments.append(DocArgument(cmd[1], cmd[2]))
				except KeyError:
					for i in range(0, len(globalMethods)):
						if globalMethods[i].name == cmd[0][1]:
							globalMethods[i].arguments.append(DocArgument(cmd[1], cmd[2]))
							break
			elif cmd[0][0] == "methodof" and p == 1:
				m = None
				if not " " in line.split("(")[0]:
					m = DocMethod(line.split(";")[0].split("(")[0].strip("*"), line.split(";")[0], cmd[1])
				else:
					m = DocMethod(line.split(";")[0].split("(")[0].split(" ")[1].strip("*"), line.split(";")[0], cmd[1])
				structs[cmd[0][1]].methods[m.name] = m
				structs[cmd[0][1]].methorder.append(m.name)
				methods[m.name] = cmd[0][1]
			elif cmd[0][0] == "method" and p == 0:
				m = DocMethod(line.split(";")[0].split("(")[0].split(" ")[1].strip("*"), line.split(";")[0], cmd[1])
				globalMethods.append(m)
			elif cmd[0][0] == "namespace" and p == 0:
				tmp = [cmd[0][1]]+cmd[1:]
				while "" in tmp:
					tmp.remove("")
				ns = ""
				for i in tmp:
					ns += "::"+i
				ns = ns[2:]
		except IndexError as e:
			print("syntax error in", cmd[0][0], "in", f, file=sys.stderr)
			print("\t"+line, file=sys.stderr)
			print("\t", e, file=sys.stderr)
		except KeyError as e:
			print("linking error in", cmd[0][0], "in", f, file=sys.stderr)
			print("\t"+line, file=sys.stderr)
			print("\t", e, "- available are:", list(methods.keys()), file=sys.stderr)

		return ns

	for p in range(0, 3):
		for line in page.split("\n"):
			if "//" in line:
				tmp = extractFromLine(line.strip(" \t"), p, path)
				if tmp != "":
					namespace = tmp
	
	rtn = globalMethods
	for i in order:
		if i in list(types.keys()):
			rtn.append(types[i])
		elif i in list(structs.keys()):
			rtn.append(structs[i])
	return (rtn, additions, namespace)

class DocDS(object):
	def __init__(self, n):
		self.name = n
		self.headers = []
		self.subdirs = {}

	def listHeaderPaths(self):
		rtn = [os.path.join(self.name, i) for i in self.headers]
		for i in sorted(list(self.subdirs.keys())):
			rtn += self.subdirs[i].listHeaderPaths()
		return rtn

	def genHTML(self, extras=[]):
		s = ""
		s += "<ul style='list-style-type:none'>"
		for i in extras:
			s += "<li><a href='javascript:showPage(\""+os.path.basename(os.path.splitext(i)[0])+"\");'>"+os.path.basename(os.path.splitext(i)[0])+"</a></li>"
		s += "<li><b>"+self.name+"</b>/</li>"
		s += "<ul style='list-style-type:none'>"
		for i in self.headers:
			s += "<li><a href='javascript:showPage(\""+os.path.join(self.name, i)+"\");'>"+i+"</a></li>"
		for i in sorted(list(self.subdirs.keys())):
			s += self.subdirs[i].genHTML()
		s += "</ul></ul>"
		return s

def getDirectoryStructure(path, rel):
	w = next(os.walk(path))
	ds = DocDS(os.path.relpath(os.path.normpath(path), rel))
	for i in w[2]:
		if os.path.splitext(i)[1] == ".h":
			ds.headers.append(i)
	ds.headers = sorted(ds.headers)
	for i in w[1]:
		if i[0] == '.':
			continue
		ds.subdirs[i] = getDirectoryStructure(os.path.join(path, i), rel)
	return ds

def directoryTreeToHTML(root, extras):
	ds = getDirectoryStructure(root, root)
	s = "<html><head><title>-_- 0.01 alpha reference</title></head>"
	s += "<link rel='stylesheet' type='text/css' href='main.css'>"
	s += "<body onload=\""
	s += "pages = "+repr(ds.listHeaderPaths()+[os.path.basename(os.path.splitext(i)[0]) for i in extras])+";"
	s += "showPage('overview');\">"
	s += "<script type='text/javascript' src='main.js'>"
	s += "</script>"
	s += "<div id='sidebar'>"
	s += "<b style='color:#666666'>-_- 0.01 alpha</b><br>"
	s += ds.genHTML(extras)
	s += "</div>"
	s += "<div id='content'>"
	for i in extras:
		f = open(i, "r")
		s += "<div id='"+os.path.basename(os.path.splitext(i)[0])+"'>"
		s += "<div id='title'>"+os.path.basename(os.path.splitext(i)[0])+"</div>"
		s += f.read()
		s += "</div>"
		f.close()
	for i in ds.listHeaderPaths():
		s += "<div id='"+i+"'>"
		s += "<div id='title'>"+os.path.splitext(i)[0]+"</div><br>"
		docs = extractDocumentationFrom(os.path.join(root, i))
		for j in docs[1]:
			s += j+"<br>"
		s += "<br><hr><br>"
		if docs[2] != "":
			s += "<b>namespace</b> - "+docs[2]+"<p>"
		for j in docs[0]:
			s += j.genHTML()
		s += "</div>"
	s += "</div>"
	s += "</body>"
	s += "</html>"
	return s

def generateTutorial(path):
	f = open(path, "r")
	p = f.read()
	f.close()

	s = "<html><head><title>-_- 0.01 alpha tutorial</title><link rel='stylesheet' type='text/css' href='main.css'></head><body style='padding-left:100px;padding-right:100px;padding-top:100px;width:800px;'><div id='title'>-_- 0.01 alpha tutorial</div><a id='top'></a><hr><div id='title'>Table of Contents:</div><div style='padding-left: 10px'><ul>"
	sections = {}
	secorder = []
	for i in p.split("[["):
		if i == "":
			continue
		tmp = i[i.find("]]")+2:]
		tmp = tmp.replace("{{", "<div id='code'>")
		tmp = tmp.replace("}}", "</div>")
		tmp = tmp.replace("\n", "<br>")
		sections[i.split("]]")[0]] = tmp
		secorder.append(i.split("]]")[0])

	for i in secorder:
		s += "<li><a href='#"+i+"'>"+i+"</a></li>"

	s += "</ul></div>"

	for i in secorder:
		s += "<div id='title'>"+i+" (<a id='"+i+"' href='#top'>top</a>)</div>"
		s += sections[i]

	s += "</body></html>"
	return s

if len(sys.argv) <= 1:
	print("usage: --generate-header-documentation <SOURCE DIRECTORY> [EXTRAS, ...]", file=sys.stderr)
	print("       --generate-tutorial-documentation <INPUT>", file=sys.stderr)
elif sys.argv[1] == "--generate-header-documentation":
	if len(sys.argv) <= 2:
		print("usage: --generate-header-documentation <SOURCE DIRECTORY> [EXTRAS, ...]", file=sys.stderr)
	else:
		print(directoryTreeToHTML(sys.argv[2], sys.argv[3:]))
elif sys.argv[1] == "--generate-tutorial-documentation":
	if len(sys.argv) <= 2:
		print("usage: --generate-tutorial-documentation <INPUT>", file=stderr)
	else:
		print(generateTutorial(sys.argv[2]))
else:
	print("usage: --generate-header-documentation <SOURCE DIRECTORY> [EXTRAS, ...]", file=sys.stderr)
	print("       --generate-tutorial-documentation <INPUT>", file=sys.stderr)